﻿using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;

namespace CryptoModule
{
    public class RSASignature
    {
        private readonly int keyLenth = 1024;

        public RSASignature(int keyLenth)
        {
            this.keyLenth = keyLenth;
        }

        public RSASignature()
        {
        }

        public SortedList<string, byte[]> GeneraitPair()
        {
            var rsa = new RSACryptoServiceProvider(keyLenth);
            return new SortedList<string, byte[]>
                           {
                               {"private", SerializePrivateKey(rsa.ExportParameters(true))},
                               {"public", SerializePublicKey(rsa.ExportParameters(false))}
                           };
        }

        private static byte[] SerializePublicKey(RSAParameters rsaParameters)
        {
            var memoryStream = new MemoryStream();
            var binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, rsaParameters.Exponent); //сериализуем публичную часть
            binaryFormatter.Serialize(memoryStream, rsaParameters.Modulus);
            byte[] publicKey = memoryStream.GetBuffer();
            memoryStream.Close();
            return publicKey;
        }

        private static byte[] SerializePrivateKey(RSAParameters rsaParameters)
        {
            var memoryStream = new MemoryStream();
            var binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, rsaParameters.P);
            binaryFormatter.Serialize(memoryStream, rsaParameters.Q);
            binaryFormatter.Serialize(memoryStream, rsaParameters.D);
            binaryFormatter.Serialize(memoryStream, rsaParameters.Exponent);
            binaryFormatter.Serialize(memoryStream, rsaParameters.InverseQ);
            binaryFormatter.Serialize(memoryStream, rsaParameters.Modulus);
            binaryFormatter.Serialize(memoryStream, rsaParameters.DP);
            binaryFormatter.Serialize(memoryStream, rsaParameters.DQ);
            byte[] pivateKey = memoryStream.GetBuffer();
            memoryStream.Close();
            return pivateKey;
        }

        private static RSAParameters DeserializePrivateKey(byte[] privateKey)
        {
            var rsaParametrs = new RSAParameters();
            var binaryFormatter = new BinaryFormatter();
            var memoryStream = new MemoryStream(privateKey, false);
            rsaParametrs.P = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.Q = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.D = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.Exponent = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.InverseQ = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.Modulus = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.DP = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.DQ = (byte[])binaryFormatter.Deserialize(memoryStream);
            memoryStream.Close();
            return rsaParametrs;
        }

        private static RSAParameters DeserializePublicKey(byte[] publicKey)
        {
            var rsaParametrs = new RSAParameters();
            var binaryFormatter = new BinaryFormatter();
            var memoryStream = new MemoryStream(publicKey, false);
            rsaParametrs.Exponent = (byte[])binaryFormatter.Deserialize(memoryStream);
            rsaParametrs.Modulus = (byte[])binaryFormatter.Deserialize(memoryStream);
            memoryStream.Close();
            return rsaParametrs;
        }

        public byte[] CreateSignature(byte[] data, byte[] privateKey)
        {
            RSAParameters rsaParameters = DeserializePrivateKey(privateKey);
            var rsa = new RSACryptoServiceProvider(keyLenth);
            rsa.ImportParameters(rsaParameters);
            return rsa.SignData(data, new MD5CryptoServiceProvider());
        }

        public bool VerifySignature(byte[] data, byte[] signature, byte[] publicKey)
        {
            RSAParameters rsaParameters = DeserializePublicKey(publicKey);
            var rsa = new RSACryptoServiceProvider(keyLenth);
            rsa.ImportParameters(rsaParameters);
            return rsa.VerifyData(data, new MD5CryptoServiceProvider(), signature);
        }
    }
}